import { ref, nextTick, onBeforeUnmount, type Ref } from 'vue';

export interface FloatingPanelOptions {
  margin?: number;
  placement?: 'top' | 'bottom' | 'auto';
  offset?: number;
}

export interface FloatingPanelPosition {
  left: number;
  top: number;
  placement: 'top' | 'bottom';
}

export function useFloatingPanel(
  triggerElement: Ref<HTMLElement | null>,
  panelElement: Ref<HTMLElement | null>,
  options: FloatingPanelOptions = {}
) {
  const { margin = 8, placement = 'auto', offset = 4 } = options;
  
  const position = ref<FloatingPanelPosition>({
    left: 0,
    top: 0,
    placement: 'bottom'
  });
  
  let repositionRafId = 0;
  let resizeObserver: ResizeObserver | null = null;
  
  function calculatePosition(): FloatingPanelPosition {
    const trigger = triggerElement.value;
    const panel = panelElement.value;
    
    if (!trigger || !panel) {
      return { left: 0, top: 0, placement: 'bottom' };
    }
    
    const triggerRect = trigger.getBoundingClientRect();
    const panelRect = panel.getBoundingClientRect();
    const vw = window.innerWidth || document.documentElement.clientWidth;
    const vh = window.innerHeight || document.documentElement.clientHeight;
    
    // Calculate horizontal position
    let left = Math.round(triggerRect.left + window.scrollX);
    const panelWidth = panelRect.width || 0;
    
    // Clamp to viewport with margin
    left = Math.max(
      window.scrollX + margin, 
      Math.min(left, window.scrollX + vw - margin - panelWidth)
    );
    
    // Calculate vertical position and placement
    const spaceBelow = vh - triggerRect.bottom;
    const spaceAbove = triggerRect.top;
    const panelHeight = panelRect.height || 0;
    
    let finalPlacement: 'top' | 'bottom' = 'bottom';
    let top: number;
    
    if (placement === 'auto') {
      // Auto placement logic
      if (spaceBelow < panelHeight + offset + margin && spaceAbove > spaceBelow) {
        finalPlacement = 'top';
        top = Math.round(triggerRect.top + window.scrollY - panelHeight - offset);
      } else {
        finalPlacement = 'bottom';
        top = Math.round(triggerRect.bottom + window.scrollY + offset);
      }
    } else {
      finalPlacement = placement;
      if (placement === 'top') {
        top = Math.round(triggerRect.top + window.scrollY - panelHeight - offset);
      } else {
        top = Math.round(triggerRect.bottom + window.scrollY + offset);
      }
    }
    
    // Clamp to viewport with margin
    const minTop = window.scrollY + margin;
    const maxTop = window.scrollY + vh - margin - panelHeight;
    top = Math.max(minTop, Math.min(top, maxTop));
    
    return { left, top, placement: finalPlacement };
  }
  
  function scheduleReposition() {
    if (repositionRafId) cancelAnimationFrame(repositionRafId);
    repositionRafId = requestAnimationFrame(() => {
      position.value = calculatePosition();
    });
  }
  
  function startTracking() {
    // Track viewport changes
    window.addEventListener('scroll', scheduleReposition, { passive: true });
    window.addEventListener('resize', scheduleReposition, { passive: true });
    
    // Track trigger element changes with ResizeObserver
    if (typeof ResizeObserver !== 'undefined' && triggerElement.value) {
      resizeObserver = new ResizeObserver(() => {
        scheduleReposition();
      });
      resizeObserver.observe(triggerElement.value);
    }
    
    // Initial positioning
    nextTick(() => {
      scheduleReposition();
    });
  }
  
  function stopTracking() {
    window.removeEventListener('scroll', scheduleReposition);
    window.removeEventListener('resize', scheduleReposition);
    
    if (resizeObserver) {
      resizeObserver.disconnect();
      resizeObserver = null;
    }
    
    if (repositionRafId) {
      cancelAnimationFrame(repositionRafId);
      repositionRafId = 0;
    }
  }
  
  onBeforeUnmount(() => {
    stopTracking();
  });
  
  return {
    position,
    startTracking,
    stopTracking,
    scheduleReposition
  };
}